//
//  CLLayoutSupportNode.m
//  ComponentLinkDemo
//
//  Created by Jeanson on 2017/3/6.
//  Copyright © 2017年 zhenby. All rights reserved.
//

#import <objc/runtime.h>
#import "CLLayoutSupportNode.h"
#import "CLPViewComponent.h"
#import "CLPViewControllerComponent.h"
#import "ASDKLink.h"

@interface CLLayoutSupportNode () <CLPTransitionContext,CLPAnimator>

@property (nonatomic,weak) id<ASContextTransitioning> asContext;

@end

@implementation CLLayoutSupportNode

- (instancetype) initWithLayoutElement:(id<CLPLayoutElement> _Nullable)layoutElement {
    self = [super init];
    if (self) {
        self.layoutElement = layoutElement;
    }
    return self;
}

- (void) layoutDidFinish
{
    [super layoutDidFinish];
    
    if (self.layoutElement.layoutView) {
        [self.layoutElement.layoutView setFrame:self.view.bounds];
    }
}

- (void) dealloc {
    
}

- (ASLayoutSpec*) layoutSpecThatFits:(ASSizeRange)constrainedSize
{
    UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
    
    NSMutableArray *absoluteSpecs = [NSMutableArray array];
    NSMutableArray *insetSpecs = [NSMutableArray array];
    NSMutableArray *stackSpecs = [NSMutableArray array];
    NSArray<id<CLPLayoutElement>> *components = self.layoutElement.subLayoutElements;
    [components enumerateObjectsUsingBlock:^(id<CLPLayoutElement>  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        id<_CLPLayoutSupporter> supporter = CLLayoutElementToLayoutSupporter(obj);
        if (supporter && [supporter conformsToProtocol:@protocol(ASLayoutElement)]) {
            id<ASLayoutElement> asLayoutElement = (id<ASLayoutElement>)supporter;
            CLLayout *layout = [obj.layoutDesc layoutThatFitsInterfaceOrientation:orientation];
            ASWrapperLayoutSpec *wrapper = [ASWrapperLayoutSpec wrapperWithLayoutElement:asLayoutElement];
            wrapper.style.preferredLayoutSize = CLLayoutSizeToASLayoutSize(layout.preferredLayoutSize);
            wrapper.style.maxLayoutSize = CLLayoutSizeToASLayoutSize(layout.maxLayoutSize);
            wrapper.style.minLayoutSize = CLLayoutSizeToASLayoutSize(layout.minLayoutSize);
            if (layout.ratio > 0) {
                ASRatioLayoutSpec *ratioSpec = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:layout.ratio child:wrapper];
                wrapper = [ASWrapperLayoutSpec wrapperWithLayoutElement:ratioSpec];
            }
            
            if (layout.positionType == CLPositionTypeAbsolute) {
                wrapper.style.layoutPosition = layout.layoutPosition;
                [absoluteSpecs addObject:wrapper];
            }
            else {
                if (!UIEdgeInsetsEqualToEdgeInsets(CLLayoutInsets(layout), UIEdgeInsetsInfinity)) {
                    ASInsetLayoutSpec *insetSpec= [ASInsetLayoutSpec insetLayoutSpecWithInsets:CLLayoutInsets(layout) child:wrapper];
                    [insetSpecs addObject:insetSpec];
                }
                else {
                    if (!UIEdgeInsetsEqualToEdgeInsets(UIEdgeInsetsZero, CLLayoutMarginInsets(layout))) {
                        ASInsetLayoutSpec *insetSpec= [ASInsetLayoutSpec insetLayoutSpecWithInsets:CLLayoutMarginInsets(layout) child:wrapper];
                        wrapper = [ASWrapperLayoutSpec wrapperWithLayoutElement:insetSpec];
                    }
                    wrapper.style.flexGrow = layout.flexGrow;
                    wrapper.style.flexBasis = CLDimensionToASDimension(layout.flexBasis);
                    wrapper.style.flexShrink = layout.flexShrink;
                    wrapper.style.spacingAfter = layout.spacingAfter;
                    wrapper.style.spacingBefore = layout.spacingBefore;
                    wrapper.style.alignSelf = (ASStackLayoutAlignSelf)layout.alignSelf;
                    wrapper.style.ascender = layout.ascender;
                    wrapper.style.descender = layout.descender;
                    [stackSpecs addObject:wrapper];
                }
            }
        }
    }];
    
    id<CLPLayoutDescription> layoutDesc = self.layoutElement.layoutDesc;
    
    CLLayout *layout = [layoutDesc layoutThatFitsInterfaceOrientation:orientation];
    
    NSMutableArray *rootSpecs = [NSMutableArray array];
    if (stackSpecs.count > 0) {
        ASStackLayoutSpec *stackLayoutSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:(ASStackLayoutDirection)layout.flexDirection spacing:layout.spacing justifyContent:(ASStackLayoutJustifyContent)layout.justifyConent alignItems:(ASStackLayoutAlignItems)layout.alignItems flexWrap:(ASStackLayoutFlexWrap)layout.flexWrap alignContent:(ASStackLayoutAlignContent)layout.alignContent children:stackSpecs];
        [rootSpecs addObject:stackLayoutSpec];
    }
    
    if (insetSpecs.count > 0) {
        [rootSpecs addObjectsFromArray:insetSpecs];
    }
    
    if (absoluteSpecs.count > 0) {
        ASAbsoluteLayoutSpec *absoluteLayoutSpec = [ASAbsoluteLayoutSpec absoluteLayoutSpecWithChildren:absoluteSpecs];
        [rootSpecs addObject:absoluteLayoutSpec];
    }
    
    if (rootSpecs.count > 0) {
        return [ASWrapperLayoutSpec wrapperWithLayoutElements:rootSpecs];
    }
    
    return [ASLayoutSpec new];
}

- (void) animateLayoutTransition:(id<ASContextTransitioning>)context
{
    self.asContext = context;
    
    if ([self.layoutElement respondsToSelector:@selector(shouldBeginTransition:)]) {
        if ([self.layoutElement shouldBeginTransition:self]) {
            if ([self.layoutElement respondsToSelector:@selector(animateTransition:)]) {
                [self.layoutElement animateTransition:self];
                return;
            }
        }
    }
    self.asContext = nil;
    
    [super animateLayoutTransition:context];
}

#pragma mark - _CLPLayoutSupporter
- (id<CLPAnimator>) animator {
    return self;
}

- (void) needsLayout {
    [self setNeedsLayout];
}

- (void) transitionLayoutWithDuration:(NSTimeInterval)duration {
    self.defaultLayoutTransitionDuration = duration;
    [self transitionLayoutWithAnimation:YES shouldMeasureAsync:NO measurementCompletion:NULL];
}

- (void) addLayoutElementToLayoutTree:(id<CLPLayoutElement>)layoutElement {
    id<_CLPLayoutSupporter> supporter = CLLayoutElementToLayoutSupporter(layoutElement);
    
    if (!supporter) {
        supporter = CLCreateLayoutSupporterWithLayoutElement(layoutElement);
    }
    
    if (supporter && [supporter isKindOfClass:[ASDisplayNode class]]) {
        ASDisplayNode *node = (ASDisplayNode*)supporter;
        [self addSubnode:node];
    }
}

- (void) layoutViewChange {
    if (_layoutElement.layoutView) {
        [self.view addSubview:_layoutElement.layoutView];
    }
}

#pragma mark -CLPTransitionContext
- (_Nullable id<CLPAnimator>) animatorForLayoutElement:(id<CLPLayoutElement>)layoutElement;
{
    id<_CLPLayoutSupporter> supporter = CLLayoutElementToLayoutSupporter(layoutElement);
    if (supporter && [self.layoutElement.subLayoutElements containsObject:layoutElement]) {
        return supporter.animator;
    }
    
    NSLog(@"have not animator for this transition view");
    return NULL;
}

- (void) completeTransion:(BOOL)finished {
    [self.asContext completeTransition:finished];
}

- (CGRect)initialFrameForAnimator:(_Nonnull id<CLPAnimator>)animator {
    return [self.asContext initialFrameForNode:(ASDisplayNode*)animator];
}

- (CGRect)finalFrameForAnimator:(_Nonnull id<CLPAnimator>)animator {
    return [self.asContext finalFrameForNode:(ASDisplayNode*)animator];
}

#pragma mark - LayoutElement
- (void) setLayoutElement:(id<CLPLayoutElement>)layoutElement
{
    [self __removeLayoutElement];
    
    _layoutElement = layoutElement;
    if (layoutElement.layoutView) {
        [self.view addSubview:layoutElement.layoutView];
    }
    CLLinkLayoutSuppoterWithLayoutElement(self, layoutElement);
}

#pragma mark - private
- (void) __removeLayoutElement {

    [_layoutElement.layoutView removeFromSuperview];
    CLUnlinkLayoutSuppoterWithLayoutElement(self, _layoutElement);
}

@end
